{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# ООП-1\n",
    "\n",
    "Булыгин Олег:  \n",
    "* [LinkedIn](linkedin.com/in/obulygin)  \n",
    "* [Мой канал в ТГ по Python](https://t.me/pythontalk_ru)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(type('Hello world'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# при помощи функции dir мы можем посмотреть все методы класса\n",
    "print(dir(str))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Создаем свой первый класс. Он пустой, но работает!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Character:\n",
    "    pass"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Объект — отдельный представитель класса, имеющий конкретное состояние и поведение, полностью определяемое классом."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "peter = Character()\n",
    "\n",
    "print(type(peter))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Атрибуты — переменные внутри класса, которая хранит какую-то информацию, о нем.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# добавим нашему классу несколько атрибутов\n",
    "class Character:\n",
    "    name = ''\n",
    "    power = 0 \n",
    "    energy = 100\n",
    "    hands = 2\n",
    "    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# а где хранятся все атрибуты после объявления класса?\n",
    "print(Character.__dict__)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# при вызове класса мы всегда создаем новый объект\n",
    "# у конкретного экземпляра будут все те же атрибуты, что и у его класса\n",
    "peter = Character()\n",
    "print(peter.name)\n",
    "print(peter.power)\n",
    "print(peter.energy)\n",
    "print(peter.hands)\n",
    "\n",
    "# они берутся именно из Character.__dict__ (т.к. не менялись)\n",
    "print(peter.__dict__)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# мы можем экземпляру присвоить свои атрибуты\n",
    "peter.name = 'Peter Parker'\n",
    "peter.power = 70"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# и даже те, которых изначально в классе нет\n",
    "peter.alias = 'Spider-Man'\n",
    "print(peter.alias)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# print(peter.name)\n",
    "# print(peter.power)\n",
    "# print(peter.energy)\n",
    "# print(peter.hands)\n",
    "\n",
    "# измененные атрибуты уже будут храниться в словаре самого экземпляра\n",
    "print(peter.__dict__)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# создадим еще один экземпляр класса\n",
    "bruce = Character()\n",
    "bruce.name = 'Bruce Wayne'\n",
    "bruce.power = 85\n",
    "bruce.alias = 'Batman'\n",
    "\n",
    "print(bruce.name)\n",
    "print(bruce.power)\n",
    "print(bruce.energy)\n",
    "print(bruce.hands)\n",
    "print(bruce.alias)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Придумаем несколько методов для нашего класса\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Character:\n",
    "    name = ''\n",
    "    power = 0\n",
    "    energy = 100\n",
    "    hands = 2\n",
    "# мы видим, что аргумент self ссылается на конкретный экземпляр класса (который еще не создан).\n",
    "# Его обязательно нужно прописывать, чтобы показывать то, \n",
    "# что все действия будут происходить именно с тем объектом, к которому мы применяем метод    \n",
    "    def eat(self, food):\n",
    "        if self.energy < 100:\n",
    "            self.energy += food\n",
    "        else:\n",
    "            print('Not hungry')\n",
    "    \n",
    "    def do_exercise(self, hours):\n",
    "        if self.energy > 0:\n",
    "            self.energy -= hours * 2\n",
    "            self.power += hours * 2\n",
    "        else:\n",
    "            print('Too tired')\n",
    "    \n",
    "    def change_alias(self, new_alias):\n",
    "        print(self) # просто посмотрим, для чего тут self?\n",
    "        self.alias = new_alias\n",
    "        \n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# еще раз проиницаилизируем создание экземпляра\n",
    "bruce = Character()\n",
    "bruce.name = 'Bruce Wayne'\n",
    "bruce.power = 85"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# пока нет псевдонима\n",
    "print(bruce.alias)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# добавим псевдоним\n",
    "bruce.change_alias('Batman')\n",
    "print(bruce.alias)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# изменим псевдоним\n",
    "bruce.change_alias('Dark Knight')\n",
    "print(bruce.alias)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Проблема с инициализацией параметров изменяемыми типами"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Character:\n",
    "    name = ''\n",
    "    power = 0\n",
    "    energy = 0\n",
    "    hands = 2\n",
    "    backpack = [] # добавляем атрибут с изменяемым типом – списком\n",
    "    \n",
    "    def eat(self, food):\n",
    "        if self.energy < 100:\n",
    "            self.energy += food\n",
    "        else:\n",
    "            print('Not hungry')\n",
    "        \n",
    "    \n",
    "    def do_exercise(self, hours):\n",
    "        if self.energy > 0:\n",
    "            self.energy -= hours * 2\n",
    "            self.power += hours * 2\n",
    "        else:\n",
    "            print('Too tired')\n",
    "    \n",
    "    def change_alias(self, new_alias):\n",
    "        self.alias = new_alias"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "peter = Character()\n",
    "bruce = Character()\n",
    "\n",
    "peter.backpack.append('web-shooters') # дадим Питеру веб-шутеры"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# проверяем\n",
    "\n",
    "print(peter.backpack)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# проверяем\n",
    "\n",
    "print(peter.backpack)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# а что с Бэтманом?\n",
    "print(bruce.backpack)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# значение инициализируется при создании класса, а изменяемые типы ссылаются на один и тот же объект в памяти\n",
    "# т.е. они будут общими у экземпляров класса. \n",
    "# Поэтому никогда не нужно делать изменяемые типы значениями по-умолчанию\n",
    "\n",
    "print(Character.__dict__)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Магический метод __init__ позволяет задать атрибуты при инициализации экземпляра класса, а также решить проблему, указанную выше"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Character:\n",
    "    def __init__(self, name, power, energy=100, hands=2):\n",
    "        # параметром по-умолчанию backpack делать не будем, чтобы он не был общим\n",
    "        self.name = name\n",
    "        self.power = power\n",
    "        self.energy = energy\n",
    "        self.backpack = [] # будем присваивать пустой список именно для КОНКРЕТНОГО экземпляра при создании (self)\n",
    "        self.hands = hands\n",
    "\n",
    "    def eat(self, food):\n",
    "        if self.energy < 100:\n",
    "            self.energy += food\n",
    "        else:\n",
    "            print('Not hungry')        \n",
    "    \n",
    "    def do_exercise(self, hours):\n",
    "        if self.energy > 0:\n",
    "            self.energy -= hours * 2\n",
    "            self.power += hours * 2\n",
    "        else:\n",
    "            print('Too tired')\n",
    "    \n",
    "    def change_alias(self, new_alias):\n",
    "        self.alias = new_alias\n",
    "        "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# теперь при создании экземпляра класса нам надо обязательно передать аргументы\n",
    "# peter = Character()\n",
    "peter = Character('Peter Parker', 80)\n",
    "bruce = Character('Bruce Wayne', 85)\n",
    "print(peter.name)\n",
    "print(peter.power)\n",
    "#если они не заданы по умолчанию\n",
    "print(peter.hands)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# при таком раскладе (init) все атрибуты сразу же попадают в словарь экземпляра (а не только измененные)\n",
    "print(peter.__dict__) "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(Character.__dict__) "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# плюс мы решим проблемы общих изменяемых атрибутов\n",
    "peter.backpack.append('web-shooters')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(peter.backpack)\n",
    "print(bruce.backpack)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Итого: в init будем прописывать то, что хотим задавать при инициализации экзмепляров класса. Все атрибуты с изменямыми значениями по-умолчанию, которые по плану будут общие для всех экзмепляров можно прописывать без него"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Взаимодействия классов: посмотрим на основе сложения"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "num1 = 5\n",
    "num2 = 10"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# числа являются экземплярами класса int\n",
    "print(type(num1))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(num1 + num2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# на самом деле происходит следующее\n",
    "print(num1.__add__(num2))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Character:\n",
    "    def __init__(self, name, power, energy=100, hands=2):\n",
    "        self.name = name\n",
    "        self.power = power\n",
    "        self.energy = energy\n",
    "        self.backpack = []\n",
    "        self.hands = hands\n",
    "    \n",
    "    def eat(self, food):\n",
    "        if self.energy < 100:\n",
    "            self.energy += food\n",
    "        else:\n",
    "            print('Not hungry')\n",
    "        \n",
    "    def do_exercise(self, hours):\n",
    "        if self.energy > 0:\n",
    "            self.energy -= hours * 2\n",
    "            self.power += hours * 2\n",
    "        else:\n",
    "            print('Too tired')\n",
    "    \n",
    "    def change_alias(self, new_alias):\n",
    "        self.alias = new_alias\n",
    "\n",
    "    # в методы мы без проблем можем передавать другие объекты и с ними взаимодействовать  \n",
    "    def beat_up(self, foe):\n",
    "        if not isinstance(foe, Character): # проверка является ли объект экземпляром указанного класса\n",
    "            return\n",
    "        if foe.power < self.power:\n",
    "            foe.status = 'defeated'\n",
    "            self.status = 'winner'\n",
    "        else:\n",
    "            print('Retreat!')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "peter = Character('Peter Parker', 80)\n",
    "bruce = Character('Bruce Wayne', 85)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "bruce.beat_up(peter)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(peter.status)\n",
    "print(bruce.status)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "peter.beat_up(bruce)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3.10.7 64-bit",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.7"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": false,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": false
  },
  "vscode": {
   "interpreter": {
    "hash": "aee8b7b246df8f9039afb4144a1f6fd8d2ca17a180786b69acc140d282b71a49"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
